package cn.seaboot.excel.sax;

import cn.seaboot.commons.core.CommonUtils;
import cn.seaboot.excel.ExcelCell;
import cn.seaboot.html.SaxHtml;
import cn.seaboot.html.table.*;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.util.CellRangeAddress;
import org.dom4j.Element;
import org.jetbrains.annotations.NotNull;

import java.io.IOException;
import java.util.List;
import java.util.Map;

/**
 * parse tag: table
 * <p>
 * 带模板功能的 html -> excel 转换
 *
 * @author Mr.css
 * @version 2025-07-01 14:37
 */
public class TableTemplate implements DocumentParser {

    /**
     * 上下文
     */
    private ExcelContext context;
    /**
     * 表头数据
     */
    private Map<String, Object> headData;
    /**
     * 表尾数据
     */
    private Map<String, Object> footData;

    /**
     * 需要写入 tbody 的数据
     */
    private List<Map<String, Object>> bodyData;
    /**
     * 列处理器
     */
    private final TableColumnParser columnParser = new TableColumnParser();

    @Override
    public void setContext(ExcelContext context) {
        this.context = context;
        columnParser.setContext(context);
    }

    /**
     * parse: table
     *
     * @param table 表格
     */
    @Override
    public void parse(Element table) throws IOException {
        for (Element ele : table.elements()) {
            String name = ele.getName();
            if ("colgroup".equals(name)) {
                columnParser.parse(ele);
            } else if ("tr".equals(name)) {
                // table 直接嵌套的 tr，处理逻辑与 thead 一致
                this.parseRow(ele, this.headData);
            } else if ("thead".equals(name)) {
                this.parseHeader(ele, this.headData);
            } else if ("tbody".equals(name)) {
                this.parseBody(ele);
            } else if ("tfoot".equals(name)) {
                this.parseFooter(ele, footData);
            } else {
                throw new IllegalArgumentException("illegal element: " + name);
            }
        }
    }

    /**
     * parse: tbody
     *
     * @param tbody -
     * @throws IOException -
     */
    public void parseBody(Element tbody) throws IOException {
        TBody source = SaxHtml.parseBody(tbody);
        for (Map<String, Object> values : bodyData) {
            for (TR tr : source.getRows()) {
                context.nextLine();
                this.parserRow(tr, values);
            }
        }
    }

    /**
     * parse: thead
     *
     * @param thead -
     * @throws IOException -
     */
    public void parseHeader(Element thead, Map<String, Object> values) throws IOException {
        THead head = SaxHtml.parseHead(thead);
        for (TR tr : head.getRows()) {
            this.parserRow(tr, values);
        }
    }

    /**
     * parse: tfoot
     *
     * @param tfoot -
     * @throws IOException -
     */
    public void parseFooter(Element tfoot, Map<String, Object> values) throws IOException {
        TFoot head = SaxHtml.parseFoot(tfoot);
        for (TR tr : head.getRows()) {
            this.parserRow(tr, values);
        }
    }

    /**
     * parse: tr
     * <p>
     * 处理 table 直接嵌套的 tr 标签
     * <p>
     * 注意：会被当成 thead 处理，而不是作为数据列表循环输出
     *
     * @param tr -
     * @throws IOException -
     */
    public void parseRow(Element tr, Map<String, Object> values) throws IOException {
        TR head = SaxHtml.parseRow(tr);
        this.parserRow(head, values);
    }

    /**
     * 处理行级数据
     *
     * @param tr     行级对象
     * @param values 需要写入的数据
     */
    public void parserRow(TR tr, Map<String, Object> values) {
        for (TD td : tr.getColumns()) {
            Cell cell = context.nextCell();

            // 合并单元格
            if (td.getRowspan() > 1 || td.getColspan() > 1) {
                CellRangeAddress address = this.createCellAddresses(cell, td.getRowspan(), td.getColspan());
                context.mergeRegion(address);
            }

            // 数据写入
            this.setValue(td, cell, values);
        }
    }

    /**
     * 根据单元格信息进行值填充
     *
     * @param td     单元格配置信息
     * @param cell   需要写入数据的单元格
     * @param values 需要写入的数据
     */
    public void setValue(TD td, Cell cell, Map<String, Object> values) {
        // 处理行级数据
        String key = td.getRef();
        if (CommonUtils.isNotEmpty(key)) {
            Object value = values.get(key);
            ExcelCell.setValueAsString(cell, value);
        } else {
            cell.setCellValue(td.getValue());
        }
    }

    /**
     * 单元格合并
     *
     * @param ec      起始单元格
     * @param rowspan 合并行数
     * @param colspan 合并列数
     * @return 合并区域信息
     */
    public @NotNull CellRangeAddress createCellAddresses(Cell ec, int rowspan, int colspan) {
        int rn = ec.getRowIndex(), cn = ec.getColumnIndex();
        // table cell will be merged
        int re = rn + rowspan - 1, ce = cn + colspan - 1;
        // create empty cell in range
        return new CellRangeAddress(rn, re, cn, ce);
    }

    // getter/setter -------------------------------------------------------------------------------------------------

    public Map<String, Object> getHeadData() {
        return headData;
    }

    public Map<String, Object> getFootData() {
        return footData;
    }

    public List<Map<String, Object>> getBodyData() {
        return bodyData;
    }

    public void setHeadData(Map<String, Object> data) {
        this.headData = data;
    }

    public void setFootData(Map<String, Object> data) {
        this.footData = data;
    }


    public void setBodyData(List<Map<String, Object>> bodyData) {
        this.bodyData = bodyData;
    }
}
